/**
 * 
 */
package com.chennan.work.jiuqi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.util.Optional;

import com.alibaba.fastjson.util.IOUtils;
import org.dom4j.Attribute;
import org.dom4j.CDATA;
import org.dom4j.Comment;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.Text;
import org.dom4j.XPath;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;


public class XMLUtils {

	public static Document createDocument() {
		return DocumentFactory.getInstance().createDocument();
	}

	public static Document createDocument(Element rootElement) {
		return DocumentFactory.getInstance().createDocument(rootElement);
	}

	public static Element createElement(String elementName) {
		return DocumentFactory.getInstance().createElement(elementName);
	}

	public static Element createElement(QName name) {
		return DocumentFactory.getInstance().createElement(name);
	}

	public static Element createElement(String localName, Namespace namespace) {
		return createElement(createQName(localName, namespace));
	}

	public static Comment createComment(String text) {
		return DocumentFactory.getInstance().createComment(text);
	}

	public static CDATA createCDATA(String text) {
		return DocumentFactory.getInstance().createCDATA(text);
	}

	public static Text createText(String text) {
		return DocumentFactory.getInstance().createText(text);
	}

	public static Attribute createAttribute(Element element, String name, String value) {
		return DocumentFactory.getInstance().createAttribute(element, name, value);
	}

	public static XPath createXPath(String xpathExpression) {
		return DocumentFactory.getInstance().createXPath(xpathExpression);
	}

	public static QName createQName(String localName, Namespace namespace) {
		return DocumentFactory.getInstance().createQName(localName, namespace);
	}

	public static QName createQName(String localName) {
		return DocumentFactory.getInstance().createQName(localName);
	}

	public static Document getDocument(File file) throws DocumentException,
			IOException {
		try (InputStream in = new FileInputStream(file)) {
			return getDocument(in);
		}
	}

	public static Element addElement(Element parent, String name) {
		Element elem = createElement(name);
		parent.add(elem);
		return elem;
	}

	public static Element addElement(Element parent, QName name) {
		Element elem = createElement(name);
		parent.add(elem);
		return elem;
	}

	public static Element addElement(Element parent, String name, Namespace ns) {
		Element elem = createElement(name, ns);
		parent.add(elem);
		return elem;
	}

	public static Element addElement(Element parent, String name, String text) {
		Element elem = addElement(parent, name);
		if (text != null) {
			elem.setText(text);
		}
		return elem;
	}

	public static Element addElement(Element parent, QName name, String text) {
		Element elem = addElement(parent, name);
		if (text != null) {
			elem.setText(text);
		}
		return elem;
	}

	public static Element addElement(Element parent, String name, Namespace ns, String text) {
		Element elem = addElement(parent, name, ns);
		if (text != null) {
			elem.setText(text);
		}
		return elem;
	}

	public static Element addCDATA(Element parent, String name, String text) {
		Element elem = addElement(parent, name);
		elem.add(createCDATA(text));
		return elem;
	}

	public static Element addCDATA(Element parent, QName name, String text) {
		Element elem = addElement(parent, name);
		elem.add(createCDATA(text));
		return elem;
	}

	public static Element addCDATA(Element parent, String name, Namespace ns, String text) {
		Element elem = addElement(parent, name, ns);
		elem.add(createCDATA(text));
		return elem;
	}

	private static SAXReader createSAXReader() {
		SAXReader reader = new SAXReader();
		reader.setMergeAdjacentText(true);
		reader.setStripWhitespaceText(true);
		reader.setEncoding("UTF-8"); //$NON-NLS-1$
		return reader;
	}

	public static Document getDocument(String filepath)
			throws DocumentException, IOException {
		return getDocument(new File(filepath));
	}

	public static Document getDocument(InputStream in) throws DocumentException {
		SAXReader reader = createSAXReader();
		return reader.read(in);
	}

	public static Document getDocument(Reader in) throws DocumentException {
		SAXReader reader = createSAXReader();
		return reader.read(in);
	}

	public static Document getDocument(URL url) throws DocumentException {
		SAXReader reader = createSAXReader();
		return reader.read(url);
	}

	public static Document getDocumentFromContent(String content) throws DocumentException {
		Reader in = null;
		try {
			in = new StringReader(content);
			return getDocument(in);
		} finally {
			IOUtils.close(in);
		}
	}

	public static void saveDocument(String filePath, Document doc) throws IOException {
		OutputStream output = null;
		try {
			output = new FileOutputStream(filePath);
			saveDocument(output, doc);
		} finally {
			IOUtils.close(output);
		}
	}

	public static void saveDocument(File file, Document doc) throws IOException {
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		OutputStream output = null;
		try {
			output = new FileOutputStream(file);
			saveDocument(output, doc);
		} finally {
			IOUtils.close(output);
		}
	}

	public static void saveDocument(OutputStream output, Document doc) throws IOException {
		saveDocument(output, doc, false);
	}

	public static void saveDocument(Writer output, Document doc) throws IOException {
		saveDocument(output, doc, false);
	}

	public static void saveDocument(OutputStream output, Document doc, boolean close) throws IOException {
		saveDocument(output, createOutputFormat(), doc, close);
	}

	public static void saveDocument(Writer output, Document doc, boolean close) throws IOException {
		saveDocument(output, createOutputFormat(), doc, close);
	}

	public static void saveDocument(OutputStream output, OutputFormat format,
			Document doc, boolean close) throws IOException {
		try {
			XMLWriter writer = new XMLWriter(output, format);
			writer.write(doc);
		} finally {
			if (close) {
				IOUtils.close(output);
			}
		}
	}

	public static void saveDocument(Writer output, OutputFormat format,
			Document doc, boolean close) throws IOException {
		try {
			XMLWriter writer = new XMLWriter(output, format);
			writer.write(doc);
		} finally {
			if (close) {
				IOUtils.close(output);
			}
		}
	}

	public static void saveDocument(OutputStream output, Element element) throws IOException {
		saveDocument(output, element, false);
	}

	public static void saveDocument(Writer output, Element element) throws IOException {
		saveDocument(output, element, false);
	}

	public static void saveDocument(OutputStream output, Element element, boolean close) throws IOException {
		saveDocument(output, createOutputFormat(), element, close);
	}

	public static void saveDocument(Writer output, Element element, boolean close) throws IOException {
		saveDocument(output, createOutputFormat(), element, close);
	}

	public static OutputFormat createOutputFormat() {
		return createOutputFormat("UTF-8"); //$NON-NLS-1$
	}

	public static OutputFormat createOutputFormat(String encoding) {
		OutputFormat format = new OutputFormat("\t", true, encoding); //$NON-NLS-1$
		format.setNewLineAfterDeclaration(false);
		return format;
	}

	public static void saveDocument(OutputStream output, OutputFormat format,
			Element element, boolean close) throws IOException {
		try {
			XMLWriter writer = new XMLWriter(output, format);
			writer.write(element);
		} finally {
			if (close) {
				IOUtils.close(output);
			}
		}
	}

	public static void saveDocument(Writer output, OutputFormat format,
			Element element, boolean close) throws IOException {
		try {
			XMLWriter writer = new XMLWriter(output, format);
			writer.write(element);
		} finally {
			if (close) {
				IOUtils.close(output);
			}
		}
	}

	public static String writeToString(Document doc) {
		StringWriter writer = new StringWriter();
		try {
			saveDocument(writer, doc);
		} catch (IOException e) {
			// ignore
		}
		return writer.toString();
	}

	public static String writeToString(Element element) {
		StringWriter writer = new StringWriter();
		try {
			saveDocument(writer, element);
		} catch (IOException e) {
			// ignore
		}
		return writer.toString();
	}

	public static void removeChildren(Element element, String name) {
		for (Element child : element.elements(name)) {
			element.remove(child);
		}
	}

	public static String escapeText(String text) {
		if (text == null) {
			return null;
		}

		StringBuilder buffer = new StringBuilder();
		char[] block = null;
		int i;
		int last = 0;
		int size = text.length();

		for (i = 0; i < size; i++) {
			String entity = null;
			char c = text.charAt(i);

			switch (c) {
				case '\\':
					entity = "\\\\";
					break;

				case '\n':
					entity = "\\n";
					break;

				case '\r':
					entity = "\\r";
					break;

				case '\t':
					entity = "\\t";
					break;
			}

			if (entity != null) {
				if (block == null) {
					block = text.toCharArray();
				}

				buffer.append(block, last, i - last);
				buffer.append(entity);
				last = i + 1;
			}
		}

		if (last == 0) {
			return text;
		}

		if (last < size) {
			if (block == null) {
				block = text.toCharArray();
			}
			buffer.append(block, last, i - last);
		}

		String answer = buffer.toString();
		return answer;
	}

	public static String unescapeText(String text) {
		if (text == null) {
			return null;
		}

		StringBuilder buffer = new StringBuilder();
		char[] block = null;
		int i;
		int last = 0;
		int size = text.length();

		for (i = 0; i < size; i++) {
			String entity = null;
			char c = text.charAt(i);
			int end = i;
			if (c == '\\') {
				if (i < size - 1) {
					i++;
				}
				c = text.charAt(i);

				switch (c) {
				case '\\':
					entity = "\\";
					break;

				case 'n':
					entity = "\n";
					break;

				case 'r':
					entity = "\r";
					break;

				case 't':
					entity = "\t";
					break;
				}
			}
			if (entity != null) {
				if (block == null) {
					block = text.toCharArray();
				}

				buffer.append(block, last, end - last);
				buffer.append(entity);
				last = i + 1;
			}
		}

		if (last == 0) {
			return text;
		}

		if (last < size) {
			if (block == null) {
				block = text.toCharArray();
			}
			buffer.append(block, last, i - last);
		}

		String answer = buffer.toString();
		return answer;
	}
	
	public static final boolean elementValueBoolean(Element e, String name, boolean defaultValue) {
		Element ce = e.element(name);
		if (ce == null) {
			return defaultValue;
		}
		return Boolean.valueOf(ce.getText());
	}

	public static final String elementTextTrim(Element e, String name, String defaultValue) {
		return elementText(e, name, defaultValue, true);
	}

	public static final String elementText(Element e, String name, String defaultValue) {
		return elementText(e, name, defaultValue, false);
	}

	public static final String elementText(Element e, String name, String defaultValue, boolean trim) {
		Element ce = e.element(name);
		if (ce == null) {
			return defaultValue;
		}
		final String text;
		if (trim) {
			text = ce.getTextTrim();
		} else {
			text = ce.getText();
		}
		if (text == null) {
			return defaultValue;
		}
		return text;
	}

	public static final int forceElementInt(Element e, String name)
			throws NullPointerException, NumberFormatException {
		Element ce = e.element(name);
		if (ce == null) {
			throw new NullPointerException(name);
		}
		final String text = ce.getText();
		return Integer.valueOf(text);
	}

	public static final String forceElementTextTrim(Element e, String name) throws NullPointerException {
		return forceElementText(e, name, true);
	}

	public static final String forceElementText(Element e, String name) throws NullPointerException {
		return forceElementText(e, name, false);
	}

	public static final String forceElementText(Element e, String name, boolean trim) throws NullPointerException {
		Element ce = e.element(name);
		if (ce == null) {
			throw new NullPointerException(name);
		}
		final String text;
		if (trim) {
			text = ce.getTextTrim();
		} else {
			text = ce.getText();
		}
		if (text == null || text.length() == 0) {
			throw new NullPointerException("illegal empty text for element '" + name + "'");
		}
		return text;
	}

	public static boolean getBoolean(String text) {
		return "1".equals(text) || "true".equalsIgnoreCase(text);
	}

	public static Boolean getNullableBoolean(Element element, String attrName) {
		String val = element.attributeValue(attrName);
		if (val != null) {
			return Boolean.valueOf(getBoolean(val));
		}
		return null;
	}

	public static boolean getBoolean(Element element, String attrName, boolean defaultValue) {
		String val = element.attributeValue(attrName);
		if (val != null) {
			return Boolean.valueOf(getBoolean(val));
		}
		return defaultValue;
	}

	public static Integer getNullableInteger(Element element, String attrName) {
		String val = element.attributeValue(attrName);
		if (val != null) {
			return Integer.valueOf(val);
		}
		return null;
	}

	public static int getInteger(Element element, String attrName, int defaultValue) {
		String val = element.attributeValue(attrName);
		if (val != null) {
			return Integer.valueOf(val);
		}
		return defaultValue;
	}

	public static Optional<String> getAttribute(Element element, String attrName) {
		return Optional.ofNullable(element.attributeValue(attrName));
	}

	public static Optional<Element> getElement(Element element, String name) {
		return Optional.ofNullable(element.element(name));
	}

}
